package com.fduss.mazegame.client.tui;

import com.fduss.mazegame.CLOptions;
import com.fduss.mazegame.client.Client;
import com.fduss.mazegame.client.Localizer;
import com.fduss.mazegame.client.MsgToUsr;
import com.fduss.mazegame.core.unit.Hero;
import com.fduss.mazegame.core.unit.feature.Icon;
import com.fduss.mazegame.server.ClientView;
import com.fduss.mazegame.server.Server;
import com.fduss.mazegame.util.Direction;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * A simple textual client.
 *
 * @author Duocai Wu
 */
public class Tui implements Client {

    protected Server server;
    protected Localizer localizer;
    private ClientView view;
    private Map<Icon, Character> iconChars = new HashMap<>();


    public Tui(CLOptions options) {
        localizer = new Localizer(options.getLanguage());
        server = new Server(options.getServerSpec()).init();
        initIconChars();
    }

    protected void initIconChars() {
        iconChars.put(Icon.EMPTY, TuiIconChar.EMPTY.getIconChar());
        iconChars.put(Icon.WALL, TuiIconChar.WALL.getIconChar());
        iconChars.put(Icon.END, TuiIconChar.END.getIconChar());
        iconChars.put(Icon.HERO, TuiIconChar.HERO.getIconChar());
        iconChars.put(Icon.FOOTPRINT, TuiIconChar.FOOT.getIconChar());
        iconChars.put(Icon.MONSTER, TuiIconChar.MONSTER.getIconChar());
        iconChars.put(Icon.TREASURE, TuiIconChar.TREASURE.getIconChar());
    }

    protected char iconToChar(Icon icon) {
        Character c = iconChars.get(icon);
        if (c == null)
            throw new IllegalArgumentException(icon.toString());
        return c;
    }

    protected void printClientView(ClientView view) {
        System.out.println();
        // we will need this for printing exterior walls
        char wall = iconToChar(Icon.WALL);
        // print north wall
        Icon[][] maze = view.getGameView();
        int numRows = maze.length;
        int numCols = maze[0].length;
        for (int c = 0; c < numCols + 2; c++) {
            System.out.print(wall);
        }
        System.out.println();
        // print tiles and objects in the maze
        for (int r = 0; r < numRows; r++) {
            System.out.print(wall); // west wall
            for (int c = 0; c < numCols; c++) {
                System.out.print(iconToChar(maze[r][c]));
            }
            System.out.print(wall); // east wall
            System.out.println();
        }
        // print south wall
        for (int c = 0; c < numCols + 2; c++) {
            System.out.print(wall);
        }
        System.out.println();
    }

    protected void showWelcomeMess() {
        Scanner scan = new Scanner(System.in);
        // print welcome message
        System.out.print(localizer.get(MsgToUsr.GREETING));
        scan.nextLine();
    }

    protected void startGameLoop() {
        Scanner scan = new Scanner(System.in);
        String line;
        boolean mustExit = false;
        view = server.getClientView();
        printClientView(view);
        do {
            if (view.isGameOver()) {
                System.out.print(localizer.get(MsgToUsr.CONGRATULATIONS));
                mustExit = true;
                continue;
            }
            if (!view.isHeroAlive()) {
                System.out.print(localizer.get(MsgToUsr.HERO_DIED));
                mustExit = true;
                continue;
            }
            System.out.print(localizer.get(MsgToUsr.ASK_FOR_COMMAND));
            line = scan.nextLine();
            if (line.length() == 0)
                continue;
            boolean ok;
            switch (line) {
                case "north":
                case "w":
                    movePlayer(Direction.NORTH);
                    break;
                case "south":
                case "s":
                    movePlayer(Direction.SOUTH);
                    break;
                case "east":
                case "d":
                    movePlayer(Direction.EAST);
                    break;
                case "west":
                case "a":
                    movePlayer(Direction.WEST);
                    break;
                case "back":
                case "b":
                    ok = server.undo();
                    printUndoResult(ok);
                    view = server.getClientView();
                    printClientView(view);
                    break;
                case "kill":
                case "k":
                    ok = server.killMonster();
                    printKillResult(ok);
                    view = server.getClientView();
                    printClientView(view);
                    break;
                case "pick":
                case "p":
                    ok = server.pickTreasure();
                    printPickResult(ok);
                    view = server.getClientView();
                    printClientView(view);
                    break;
                case "check":
                case "c":
                    showScore(server.checkScore());
                    break;
                case "quit":
                case "q":
                case "exit":
                case "x":
                    System.out.print(localizer.get(MsgToUsr.EXITING));
                    mustExit = true;
                    break;
                case "help":
                case "h":
                    System.out.print(String.format(
                            localizer.get(MsgToUsr.HELP),
                            TuiIconChar.WALL.getIconChar(),
                            TuiIconChar.HERO.getIconChar(),
                            TuiIconChar.END.getIconChar(),
                            TuiIconChar.FOOT.getIconChar(),
                            TuiIconChar.MONSTER.getIconChar(),
                            TuiIconChar.TREASURE.getIconChar()
                    ));
                    break;
                default:
                    System.out.print(localizer.get(MsgToUsr.UNKNOWN_COMMAND) +
                            line +
                            localizer.get(MsgToUsr.UNKNOWN_COMMAND_SUFIX));
            }
        } while (!mustExit);
    }

    public void run() {
        showWelcomeMess();
        startGameLoop();
    }

    protected void showScore(Hero hero) {
        System.out.println(String.format(
                localizer.get(MsgToUsr.SCORE),
                hero.getSteps(),
                hero.getKilling(),
                hero.getPicking()
        ));
    }

    protected void movePlayer(Direction d) {
        boolean ok = server.moveHero(d);
        printMoveResult(ok);
        view = server.getClientView();
        printClientView(view);
    }

    protected void printKillResult(boolean ok) {
        System.out.println();
        if (ok)
            System.out.println(localizer.get(MsgToUsr.KILL_MONSTER_OK));
        else
            System.out.println(localizer.get(MsgToUsr.KILL_MONSTER_KO));
    }

    protected void printPickResult(boolean ok) {
        System.out.println();
        if (ok)
            System.out.println(localizer.get(MsgToUsr.PICK_TREASURE_OK));
        else
            System.out.println(localizer.get(MsgToUsr.PICK_TREASURE_KO));
    }

    protected void printUndoResult(boolean ok) {
        System.out.println();
        if (ok)
            System.out.println(localizer.get(MsgToUsr.UNDO_OK));
        else
            System.out.println(localizer.get(MsgToUsr.UNDO_KO));
    }

    protected void printMoveResult(boolean ok) {
        System.out.println();
        if (ok)
            System.out.println(localizer.get(MsgToUsr.MOVE_OK));
        else
            System.out.println(localizer.get(MsgToUsr.MOVE_KO));
    }
}
